package com.mspbots.cw.amqp.receivers;

import com.fasterxml.jackson.databind.JsonNode;
import com.mspbots.cw.amqp.BaseUserReceiver;
import com.mspbots.cw.amqp.model.ExceptionLog;
import com.mspbots.cw.amqp.model.TenantUser;
import com.mspbots.cw.amqp.model.UsersMapping;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * com.mspbots.core.connectwise.receivers.TenantUserReceiver
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/29
 */
@Log4j2
@Component
public class UsersReceiver extends BaseUserReceiver {

    @RabbitListener(queues = "wise.users")
    public void processMessage(TenantUser content) {
        try{
            content.setIdentifier(content.getExtend().get("identifier").asText());

            Optional<Long> id = this.databaseClient.execute("SELECT id FROM tenant_user WHERE tenant_id =:tenantId AND cw_uid =:cwId")
                    .as(Long.class)
                    .bind("tenantId", content.getTenantId())
                    .bind("cwId", content.getCwUid())
                    .fetch().one().blockOptional(Duration.ofSeconds(10));
            if (id.isPresent() && id.get() > 0) {
                log.debug("Receiver connect wise user update by id {}", content);
                content.setId(id.get());
                content.setSyncTime(LocalDateTime.now());
                this.databaseClient.update().table(TenantUser.class).using(content).fetch().rowsUpdated().block(Duration.ofSeconds(10));
            } else {
                this.databaseClient.insert().into(TenantUser.class).using(content).fetch().rowsUpdated().block(Duration.ofSeconds(10));
            }
            this.databaseClient.select().from(TenantUser.class)
                    .matching(Criteria.where("tenant_id").is(content.getTenantId())
                            .and("cw_uid").is(content.getCwUid())).fetch().one()
                    .subscribe(result -> {
                                this.autoMapping(result);
                                log.debug("Wise user save success.{}", result);
                            },
                            err -> log.error("Wise user is error,msg: {}", err.getMessage()));
//        content.setExtend(null);
//        super.messagingTemplate.convertAndSend("next-ticket.redis.users",content);
        }catch (Exception e){
            this.databaseClient.insert().into(ExceptionLog.class).using(
                    ExceptionLog.builder()
                            .tenantId(content.getTenantId().intValue())
                            .name("wise.users")
                            .status(500)
                            .type("wise.users")
                            .msg(e.getMessage()+","+objectMapper.convertValue(content, JsonNode.class).toString() )
                            .build()
            ).fetch().rowsUpdated().block(Duration.ofSeconds(10));
        }
    }

    private void autoMapping(TenantUser user) {
        this.databaseClient.execute("SELECT b.id AS usersId,a.id AS cwUserId,c.teams_user_id AS teamsUserId,d.id AS tsheetUserId  " +
                "FROM tenant_user a LEFT JOIN users b ON a.email  ILIKE b.username  AND a.tenant_id=b.tenant_id  " +
                "LEFT JOIN tenant_teams_user c ON a.email ILIKE c.teams_mail  AND a.tenant_id=c.tenant_id " +
                "LEFT JOIN tsheet_users d ON a.email ILIKE d.email  AND a.tenant_id=d.tenant_id " +
                "WHERE a.tenant_id=:tenantId AND a.id = :id LIMIT 1")
                .bind("id", user.getId())
                .bind("tenantId", user.getTenantId())
                .map((row, rowMetadata) -> {
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("usersId", row.get("usersId"));
                    resultMap.put("cwUserId", row.get("cwUserId"));
                    resultMap.put("teamsUserId", row.get("teamsUserId"));
                    resultMap.put("tsheetUserId", row.get("tsheetUserId"));
                    return resultMap;
                }).all()
                .flatMap(map -> {
                    StringBuilder stringBuffer = new StringBuilder("where tenant_id = " + user.getTenantId());
                    UsersMapping usersMapping = UsersMapping.builder().tenantId(user.getTenantId())
                            .tenantUserId(user.getId()).build();
                    stringBuffer.append(" and ( tenant_user_id=").append(user.getId());

                    if (!ObjectUtils.isEmpty(map.get("usersId"))) {
                        stringBuffer.append(" or user_id = ").append(map.get("usersId"));
                        usersMapping.setUserId(Integer.valueOf(map.get("usersId").toString()));
                    }
                    if (!ObjectUtils.isEmpty(map.get("teamsUserId"))) {
                        stringBuffer.append(" or teams_user_id = '").append(map.get("teamsUserId")).append("' ");
                        usersMapping.setTeamsUserId(map.get("teamsUserId").toString());
                    }
                    if (!ObjectUtils.isEmpty(map.get("tsheetUserId"))) {
                        stringBuffer.append(" or sheet_user_id = ").append(map.get("tsheetUserId"));
                        usersMapping.setSheetUserId(Long.valueOf(map.get("tsheetUserId").toString()));
                    }
                    stringBuffer.append(")");

                    return this.databaseClient.execute("SELECT * FROM tenant_user_mapping " + stringBuffer.toString())
                            .as(UsersMapping.class)
                            .fetch().all()
                            .flatMap(mapping -> {
                                if (ObjectUtils.isEmpty(mapping.getTenantUserId())) {
                                    mapping.setTenantUserId(user.getId());
                                }
                                return this.databaseClient.update().table(UsersMapping.class).using(mapping).fetch().rowsUpdated();
                            })
                            .switchIfEmpty(this.databaseClient.insert().into(UsersMapping.class).using(usersMapping)
                                    .fetch().rowsUpdated());
                })
                .subscribe(result -> log.debug("Wise user mapping success.{}", result),
                        err -> log.error("Wise user is error,msg: {}", err.getMessage()));

    }
}
